/**
  * Author: Mei
  * Date: 2020-12-08 19:22
  * Description: 
  * 交替打印
  */
#include <thread>
#include <iostream>
#include <semaphore.h>
#include <mutex>
#include <condition_variable>
#include <functional>

using namespace std;

class ZeroEvenOdd2 {
private:
    int n;
    int i;
    sem_t sem1;
    sem_t sem2;
    sem_t sem3;
public:
    ZeroEvenOdd2(int n) {
        this->n = n;
        sem_init(&sem1, 0, 1);
        sem_init(&sem2, 0, 0);
        sem_init(&sem3, 0, 0);
    }

    void zero(function<void(int)> printNumber) { //每次都要打印0
        for (int i = 1; i <= n; ++i) {
            sem_wait(&sem1);
            printNumber(0);
            if (i % 2 == 0) {
                sem_post(&sem2);
            } else {
                sem_post(&sem3);
            }
        }
    }

    void even(function<void(int)> printNumber) { //打印奇数
        for (i = 1; i < n; i += 2) {
            sem_wait(&sem3);
            printNumber(i);
            sem_post(&sem1);
        }
    }

    void odd(function<void(int)> printNumber) { //打印偶数
        for (i = 2; i < n; i += 2) {
            sem_wait(&sem2);
            printNumber(i);
            sem_post(&sem1);
        }
    }
};

class ZeroEvenOdd {
private:
    int n;
    std::condition_variable g_cv;
    std::mutex mt;
    bool ze, other;
public:
    ZeroEvenOdd(int n) : g_cv(), mt(), ze(false), other(true) {
        this->n = n;
    }
    void zero(function<void(int)> printNumber) {
        for (int i = 1; i <= n; ++i) {
            std::unique_lock<std::mutex> lk(mt);
            g_cv.wait(lk, [this]() { return !ze && other; });  //捕捉所有值传递 false就会阻塞
            printNumber(0);
            if (i & 1)ze = true;
            else other = false;
            g_cv.notify_all();
        }
    }

    void even(function<void(int)> printNumber) {
        for (int i = 2; i <= n; i += 2) {
            std::unique_lock<std::mutex> lk(mt);
            g_cv.wait(lk, [this]() { return !ze && !other; });
            printNumber(i);
            ze = false;
            other = true;
            g_cv.notify_all();
        }
    }

    void odd(function<void(int)> printNumber) {
        for (int i = 1; i <= n; i += 2) {
            std::unique_lock<std::mutex> lk(mt);
            g_cv.wait(lk, [this]() { return ze && other; });
            printNumber(i);
            ze = false;
            g_cv.notify_all();
        }
    }
};

class ZeroEvenOdd1 {
private:
    int n;
    sem_t zero_sem;
    sem_t even_sem;
    sem_t odd_sem;
public:
    ZeroEvenOdd1(int n) {
        this->n = n;
        sem_init(&zero_sem, 0, 1);
        sem_init(&even_sem, 0, 0);
        sem_init(&odd_sem, 0, 0);
    }

    void zero(function<void(int)> printNumber) { //每次都要打印0
        for (int i = 1; i <= n; ++i) {
            sem_wait(&zero_sem);
            printNumber(0);
            if (i & 1) sem_post(&odd_sem);
            else sem_post(&even_sem);
        }
    }

    void odd(function<void(int)> printNumber) { //打印奇数
        for (int i = 1; i <= n; i += 2) {
            sem_wait(&odd_sem);
            printNumber(i);
            sem_post(&zero_sem);
        }
    }

    void even(function<void(int)> printNumber) { //打印偶数
        for (int i = 2; i <= n; i += 2) {
            sem_wait(&even_sem);
            printNumber(i);
            sem_post(&zero_sem);
        }
    }
};

void printZero(int n) {
    cout << n;
}

void printEven(int n) {
    cout << n;
}

void printOdd(int n) {
    cout << n;
}

int main() {
    ZeroEvenOdd foo(5);
    thread f{&ZeroEvenOdd::zero, &foo, printZero};
    thread f2{&ZeroEvenOdd::even, &foo, printEven};
    thread f3{&ZeroEvenOdd::odd, &foo, printOdd};

    f.join();
    f2.join();
    f3.join();

    return 0;
}
